# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
"""
Data models for XML files required for generating a preloader.

These classes encapsulate the complexities of XML DOM in order to
make retrieving data from XML files easier and more reliable.
By shielding data model deserialization from data consumers,
it'd be easier to switch to other formats such as JSON if required.

There are some assumptions about how these XML files are structured
such as the hierarchy of elements and ordering of attributes, these
are relatively safe assumptions for as long as the XML files are
always generated by HPS megawizard (isw.tcl) and are not hand-edited.

Copyright (C) 2022 Intel Corporation <www.intel.com>

Author: Lee, Kah Jing <kah.jing.lee@intel.com>
"""
import xml.dom.minidom

def getSingletonElementByTagName(parent, tagName):
	"""
	Find tag by name and ensure that there is exactly one match
	"""
	nodes = parent.getElementsByTagName(tagName)

	if len(nodes) == 0:
		raise Exception("Can't find element: " + tagName)
	elif len(nodes) > 1:
		raise Exception("Unexpected multiple matches for singleton element: " + tagName)
	else:
		return nodes[0]

class hps(object):
	"""
	Data model for hps.xml
	"""
	@staticmethod
	def create(file):
		""" hps model """
		return hps(file)

	def __init__(self, file):
		""" hps model initialization """
		self.dom = xml.dom.minidom.parse(file)

		try:
			# Look for <hps> node
			self.hpsNode = getSingletonElementByTagName(self.dom, "hps")
			# Look for <hps><system> node
			self.hpsSystemNode = getSingletonElementByTagName(self.hpsNode, "system")
		except Exception:
			raise Exception("Can't initialize from file: " + file)

	def getSystemConfig(self, param):
		""" parse system configuration tag """
		hpsSystemConfigNode = None

		# Look for <hps><system><config ...> nodes
		for node in self.hpsSystemNode.getElementsByTagName("config"):
			# assume name is the first attribute as in <config name="..." ...>
			nameAttrNode = node.attributes.item(0)
			if nameAttrNode.nodeName == "name" and nameAttrNode.nodeValue == param:
				# assume value is the second attribute as in <config name="..." value="...">
				valueAttrNode = node.attributes.item(1)
				if valueAttrNode.nodeName == "value":
					hpsSystemConfigNode = valueAttrNode
					break

		if hpsSystemConfigNode == None:
			raise ValueError("Can't find <hps><system><config> node: " + param)

		return hpsSystemConfigNode.nodeValue

class emif(object):
	"""
	Data model for emif.xml.
	"""
	@staticmethod
	def create(file):
		""" emif model """
		return emif(file)

	def __init__(self, file):
		""" emif model initialization """
		self.dom = xml.dom.minidom.parse(file)

		try:
			# Look for <emif> node
			self.emifNode = getSingletonElementByTagName(self.dom, "emif")
			# Look for <emif><pll> node
			self.emifPllNode = getSingletonElementByTagName(self.emifNode, "pll")
		except Exception:
			raise Exception("Can't initialize from file: " + file)

	def getPllDefine(self, param):
		""" parse pll define tag """
		emifPllDefineNode = None

		# Look for <emif><pll><define ...> nodes
		for node in self.emifPllNode.getElementsByTagName("define"):
			nameAttrNode = node.attributes.item(0)
			# assume name is the first attribute as in <define name="..." ...>
			if nameAttrNode.nodeName == "name" and nameAttrNode.nodeValue == param:
				# assume value is the second attribute as in <config name="..." value="...">
				valueAttrNode = node.attributes.item(1)
				if valueAttrNode.nodeName == "value":
					emifPllDefineNode = valueAttrNode
					break

		if emifPllDefineNode == None:
			raise Exception("Can't find EMIF PLL define node: " + param)

		return emifPllDefineNode.nodeValue
